﻿using System;
using System.Collections.Generic;
using System.Xml;
using GeoAPI;
using GeoAPI.Geometries;
using GeoAPI.Operation.Buffer;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries.Utilities;
using NetTopologySuite.IO;
using NetTopologySuite.IO.GML2;
using NetTopologySuite.Operation;
using NetTopologySuite.Operation.Buffer;
using NetTopologySuite.Operation.Distance;
using NetTopologySuite.Operation.Overlay;
using NetTopologySuite.Operation.Overlay.Snap;
using NetTopologySuite.Operation.Predicate;
using NetTopologySuite.Operation.Relate;
using NetTopologySuite.Operation.Union;
using NetTopologySuite.Operation.Valid;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Geometries
{
	[Serializable]
	public abstract class Geometry : IGeometry, ICloneable, IComparable, IComparable<IGeometry>, IEquatable<IGeometry>
	{
		public IGeometryFactory Factory
		{
			get
			{
				return this._factory;
			}
		}

		public object UserData
		{
			get
			{
				return this._userData;
			}
			set
			{
				this._userData = value;
			}
		}

		public int SRID
		{
			get
			{
				return this._srid;
			}
			set
			{
				this._srid = value;
				this._factory = GeometryServiceProvider.Instance.CreateGeometryFactory(this._factory.PrecisionModel, value, this._factory.CoordinateSequenceFactory);
				IGeometryCollection geometryCollection = this as IGeometryCollection;
				if (geometryCollection == null)
				{
					return;
				}
				IGeometry[] geometries = geometryCollection.Geometries;
				for (int i = 0; i < geometries.Length; i++)
				{
					geometries[i].SRID = value;
				}
			}
		}

		protected Geometry(IGeometryFactory factory)
		{
			this._factory = factory;
			this._srid = factory.SRID;
		}

		public abstract string GeometryType { get; }

		public abstract OgcGeometryType OgcGeometryType { get; }

		protected static bool HasNonEmptyElements(IGeometry[] geometries)
		{
			for (int i = 0; i < geometries.Length; i++)
			{
				if (!geometries[i].IsEmpty)
				{
					return true;
				}
			}
			return false;
		}

		public static bool HasNullElements(object[] array)
		{
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == null)
				{
					return true;
				}
			}
			return false;
		}

		public IPrecisionModel PrecisionModel
		{
			get
			{
				return this.Factory.PrecisionModel;
			}
		}

		public abstract Coordinate Coordinate { get; }

		public abstract Coordinate[] Coordinates { get; }

		public abstract double[] GetOrdinates(Ordinate ordinate);

		public abstract int NumPoints { get; }

		public virtual int NumGeometries
		{
			get
			{
				return 1;
			}
		}

		public virtual IGeometry GetGeometryN(int n)
		{
			return this;
		}

		public bool IsSimple
		{
			get
			{
				return new IsSimpleOp(this).IsSimple();
			}
		}

		public virtual bool IsValid
		{
			get
			{
				return new IsValidOp(this).IsValid;
			}
		}

		public abstract bool IsEmpty { get; }

		public double Distance(IGeometry g)
		{
			return DistanceOp.Distance(this, g);
		}

		public bool IsWithinDistance(IGeometry geom, double distance)
		{
			return this.EnvelopeInternal.Distance(geom.EnvelopeInternal) <= distance && DistanceOp.IsWithinDistance(this, geom, distance);
		}

		public virtual double Area
		{
			get
			{
				return 0.0;
			}
		}

		public virtual double Length
		{
			get
			{
				return 0.0;
			}
		}

		public IPoint Centroid
		{
			get
			{
				if (this.IsEmpty)
				{
                    return this.Factory.CreatePoint((ICoordinateSequence)null);
				}
				return Geometry.CreatePointFromInternalCoord(NetTopologySuite.Algorithm.Centroid.GetCentroid(this), this);
			}
		}

		public IPoint InteriorPoint
		{
			get
			{
				if (this.IsEmpty)
				{
                    return this.Factory.CreatePoint((ICoordinateSequence)null);
				}
				Dimension dimension = this.Dimension;
				Coordinate interiorPoint;
				if (dimension == Dimension.Point)
				{
					interiorPoint = new InteriorPointPoint(this).InteriorPoint;
				}
				else if (dimension == Dimension.Curve)
				{
					interiorPoint = new InteriorPointLine(this).InteriorPoint;
				}
				else
				{
					interiorPoint = new InteriorPointArea(this).InteriorPoint;
				}
				return Geometry.CreatePointFromInternalCoord(interiorPoint, this);
			}
		}

		public IPoint PointOnSurface
		{
			get
			{
				return this.InteriorPoint;
			}
		}

		public virtual Dimension Dimension
		{
			get
			{
				return this._dimension;
			}
			set
			{
				this._dimension = value;
			}
		}

		public virtual IGeometry Boundary { get; set; }

		public virtual Dimension BoundaryDimension { get; set; }

		public IGeometry Envelope
		{
			get
			{
				return this.Factory.ToGeometry(this.EnvelopeInternal);
			}
		}

		public Envelope EnvelopeInternal
		{
			get
			{
				if (this._envelope == null)
				{
					this._envelope = this.ComputeEnvelopeInternal();
				}
				return new Envelope(this._envelope);
			}
		}

		public void GeometryChanged()
		{
			this.Apply(new Geometry.GeometryChangedFilter());
		}

		public void GeometryChangedAction()
		{
			this._envelope = null;
		}

		public bool Disjoint(IGeometry g)
		{
			return !this.EnvelopeInternal.Intersects(g.EnvelopeInternal) || this.Relate(g).IsDisjoint();
		}

		public bool Touches(IGeometry g)
		{
			return this.EnvelopeInternal.Intersects(g.EnvelopeInternal) && this.Relate(g).IsTouches(this.Dimension, g.Dimension);
		}

		public bool Intersects(IGeometry g)
		{
			if (!this.EnvelopeInternal.Intersects(g.EnvelopeInternal))
			{
				return false;
			}
			if (this.IsRectangle)
			{
				return RectangleIntersects.Intersects((IPolygon)this, g);
			}
			if (g.IsRectangle)
			{
				return RectangleIntersects.Intersects((IPolygon)g, this);
			}
			return this.Relate(g).IsIntersects();
		}

		public bool Crosses(IGeometry g)
		{
			return this.EnvelopeInternal.Intersects(g.EnvelopeInternal) && this.Relate(g).IsCrosses(this.Dimension, g.Dimension);
		}

		public bool Within(IGeometry g)
		{
			return g.Contains(this);
		}

		public bool Contains(IGeometry g)
		{
			if (!this.EnvelopeInternal.Contains(g.EnvelopeInternal))
			{
				return false;
			}
			if (this.IsRectangle)
			{
				return RectangleContains.Contains((IPolygon)this, g);
			}
			return this.Relate(g).IsContains();
		}

		public bool Overlaps(IGeometry g)
		{
			return this.EnvelopeInternal.Intersects(g.EnvelopeInternal) && this.Relate(g).IsOverlaps(this.Dimension, g.Dimension);
		}

		public bool Covers(IGeometry g)
		{
			return this.EnvelopeInternal.Covers(g.EnvelopeInternal) && (this.IsRectangle || this.Relate(g).IsCovers());
		}

		public bool CoveredBy(IGeometry g)
		{
			return g.Covers(this);
		}

		public bool Relate(IGeometry g, string intersectionPattern)
		{
			return this.Relate(g).Matches(intersectionPattern);
		}

		public IntersectionMatrix Relate(IGeometry g)
		{
			this.CheckNotGeometryCollection(this);
			this.CheckNotGeometryCollection(g);
			return RelateOp.Relate(this, g);
		}

		public bool Equals(IGeometry g)
		{
			return g != null && this.EqualsTopologically(g);
		}

		public bool EqualsTopologically(IGeometry g)
		{
			return this.EnvelopeInternal.Equals(g.EnvelopeInternal) && this.Relate(g).IsEquals(this.Dimension, g.Dimension);
		}

		public override bool Equals(object o)
		{
			IGeometry geometry = o as IGeometry;
			return geometry != null && this.EqualsExact(geometry);
		}

		private static bool CompareGeometryCollections(IGeometry obj1, IGeometry obj2)
		{
			IGeometryCollection geometryCollection = obj1 as IGeometryCollection;
			IGeometryCollection geometryCollection2 = obj2 as IGeometryCollection;
			if (geometryCollection == null || geometryCollection2 == null)
			{
				return false;
			}
			if (geometryCollection.NumGeometries != geometryCollection2.NumGeometries)
			{
				return false;
			}
			for (int i = 0; i < geometryCollection.NumGeometries; i++)
			{
				IEquatable<IGeometry> equatable = geometryCollection[i];
				IGeometry other = geometryCollection2[i];
				if (!equatable.Equals(other))
				{
					return false;
				}
			}
			return true;
		}

		public static bool operator ==(Geometry obj1, IGeometry obj2)
		{
			return object.Equals(obj1, obj2);
		}

		public static bool operator !=(Geometry obj1, IGeometry obj2)
		{
			return !(obj1 == obj2);
		}

		public override int GetHashCode()
		{
			return this.EnvelopeInternal.GetHashCode();
		}

		public override string ToString()
		{
			return this.ToText();
		}

		public string ToText()
		{
			return new WKTWriter(3).Write(this);
		}

		public string AsText()
		{
			return this.ToText();
		}

		public byte[] ToBinary()
		{
			return new WKBWriter().Write(this);
		}

		public byte[] AsBinary()
		{
			return this.ToBinary();
		}

		public XmlReader ToGMLFeature()
		{
			return new GMLWriter().Write(this);
		}

		public IGeometry Buffer(double distance)
		{
			return BufferOp.Buffer(this, distance);
		}

		[Obsolete]
		public IGeometry Buffer(double distance, BufferStyle endCapStyle)
		{
			return BufferOp.Buffer(this, distance, 8, endCapStyle);
		}

		public IGeometry Buffer(double distance, EndCapStyle endCapStyle)
		{
			return BufferOp.Buffer(this, distance, 8, (BufferStyle)endCapStyle);
		}

		public IGeometry Buffer(double distance, int quadrantSegments)
		{
			return BufferOp.Buffer(this, distance, quadrantSegments);
		}

		[Obsolete]
		public IGeometry Buffer(double distance, int quadrantSegments, BufferStyle endCapStyle)
		{
			return BufferOp.Buffer(this, distance, quadrantSegments, endCapStyle);
		}

		public IGeometry Buffer(double distance, int quadrantSegments, EndCapStyle endCapStyle)
		{
			return BufferOp.Buffer(this, distance, quadrantSegments, (BufferStyle)endCapStyle);
		}

		public IGeometry Buffer(double distance, IBufferParameters bufferParameters)
		{
			return BufferOp.Buffer(this, distance, bufferParameters);
		}

		public virtual IGeometry ConvexHull()
		{
			return new ConvexHull(this).GetConvexHull();
		}

		public abstract IGeometry Reverse();

		public IGeometry Intersection(IGeometry other)
		{
			if (this.IsEmpty || other.IsEmpty)
			{
				return OverlayOp.CreateEmptyResult(SpatialFunction.Intersection, this, other, this._factory);
			}
			if (this.IsGeometryCollection)
			{
				IGeometry g2 = other;
				return GeometryCollectionMapper.Map((IGeometryCollection)this, (IGeometry g) => g.Intersection(g2));
			}
			this.CheckNotGeometryCollection(this);
			this.CheckNotGeometryCollection(other);
			return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.Intersection);
		}

		public IGeometry Union(IGeometry other)
		{
			if (this.IsEmpty || other == null || other.IsEmpty)
			{
				if (this.IsEmpty && (other == null || other.IsEmpty))
				{
					return OverlayOp.CreateEmptyResult(SpatialFunction.Union, this, other, this._factory);
				}
				if (other == null || other.IsEmpty)
				{
					return (IGeometry)this.Clone();
				}
				if (this.IsEmpty)
				{
					return (IGeometry)other.Clone();
				}
			}
			this.CheckNotGeometryCollection(this);
			this.CheckNotGeometryCollection(other);
			return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.Union);
		}

		public IGeometry Difference(IGeometry other)
		{
			if (this.IsEmpty)
			{
				return OverlayOp.CreateEmptyResult(SpatialFunction.Difference, this, other, this._factory);
			}
			if (other == null || other.IsEmpty)
			{
				return (IGeometry)this.Clone();
			}
			this.CheckNotGeometryCollection(this);
			this.CheckNotGeometryCollection(other);
			return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.Difference);
		}

		public IGeometry SymmetricDifference(IGeometry other)
		{
			if (this.IsEmpty || other == null || other.IsEmpty)
			{
				if (this.IsEmpty && (other == null || other.IsEmpty))
				{
					return OverlayOp.CreateEmptyResult(SpatialFunction.SymDifference, this, other, this._factory);
				}
				if (other == null || other.IsEmpty)
				{
					return (IGeometry)this.Clone();
				}
				if (this.IsEmpty)
				{
					return (IGeometry)other.Clone();
				}
			}
			this.CheckNotGeometryCollection(this);
			this.CheckNotGeometryCollection(other);
			return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.SymDifference);
		}

		public IGeometry Union()
		{
			return UnaryUnionOp.Union(this);
		}

		public abstract bool EqualsExact(IGeometry other, double tolerance);

		public bool EqualsExact(IGeometry other)
		{
			return this == other || this.EqualsExact(other, 0.0);
		}

		public bool EqualsNormalized(IGeometry g)
		{
			return g != null && this.Normalized().EqualsExact(g.Normalized());
		}

		public abstract void Apply(ICoordinateFilter filter);

		public abstract void Apply(ICoordinateSequenceFilter filter);

		public abstract void Apply(IGeometryFilter filter);

		public abstract void Apply(IGeometryComponentFilter filter);

		public virtual object Clone()
		{
			Geometry geometry = (Geometry)base.MemberwiseClone();
			if (geometry._envelope != null)
			{
				geometry._envelope = new Envelope(geometry._envelope);
			}
			return geometry;
		}

		public abstract void Normalize();

		public IGeometry Normalized()
		{
			IGeometry geometry = (IGeometry)this.Clone();
			geometry.Normalize();
			return geometry;
		}

		public int CompareTo(object o)
		{
			return this.CompareTo(o as IGeometry);
		}

		public int CompareTo(IGeometry geom)
		{
			Geometry geometry = geom as Geometry;
			if (geometry == null)
			{
				return -1;
			}
			if (this.ClassSortIndex != geometry.ClassSortIndex)
			{
				return this.ClassSortIndex - geometry.ClassSortIndex;
			}
			if (this.IsEmpty && geometry.IsEmpty)
			{
				return 0;
			}
			if (this.IsEmpty)
			{
				return -1;
			}
			if (geometry.IsEmpty)
			{
				return 1;
			}
			return this.CompareToSameClass(geom);
		}

		public int CompareTo(object o, IComparer<ICoordinateSequence> comp)
		{
			Geometry geometry = (Geometry)o;
			if (this.ClassSortIndex != geometry.ClassSortIndex)
			{
				return this.ClassSortIndex - geometry.ClassSortIndex;
			}
			if (this.IsEmpty && geometry.IsEmpty)
			{
				return 0;
			}
			if (this.IsEmpty)
			{
				return -1;
			}
			if (geometry.IsEmpty)
			{
				return 1;
			}
			return this.CompareToSameClass(o, comp);
		}

		protected virtual bool IsEquivalentClass(IGeometry other)
		{
			return base.GetType().FullName == other.GetType().FullName;
		}

		protected void CheckNotGeometryCollection(IGeometry g)
		{
			if (Geometry.IsNonHomogenousGeometryCollection(g))
			{
				throw new ArgumentException("This method does not support GeometryCollection arguments");
			}
		}

		private static bool IsNonHomogenousGeometryCollection(IGeometry g)
		{
			return g is IGeometryCollection && g.GeometryType == "GeometryCollection";
		}

		protected bool IsGeometryCollection
		{
			get
			{
				return this.OgcGeometryType == OgcGeometryType.GeometryCollection;
			}
		}

		protected abstract Envelope ComputeEnvelopeInternal();

		protected internal abstract int CompareToSameClass(object o);

		protected internal abstract int CompareToSameClass(object o, IComparer<ICoordinateSequence> comp);

		protected static int Compare(List<IGeometry> a, List<IGeometry> b)
		{
			IEnumerator<IGeometry> enumerator = a.GetEnumerator();
			IEnumerator<IGeometry> enumerator2 = b.GetEnumerator();
			while (enumerator.MoveNext() && enumerator2.MoveNext())
			{
				IComparable comparable = enumerator.Current;
				IComparable obj = enumerator2.Current;
				int num = comparable.CompareTo(obj);
				if (num != 0)
				{
					return num;
				}
			}
			if (enumerator.MoveNext())
			{
				return 1;
			}
			if (enumerator2.MoveNext())
			{
				return -1;
			}
			return 0;
		}

		protected static bool Equal(Coordinate a, Coordinate b, double tolerance)
		{
			if (tolerance == 0.0)
			{
				return a.Equals(b);
			}
			return a.Distance(b) <= tolerance;
		}

		private int ClassSortIndex
		{
			get
			{
				for (int i = 0; i < Geometry._sortedClasses.Length; i++)
				{
					if (base.GetType() == Geometry._sortedClasses[i])
					{
						return i;
					}
				}
				Assert.ShouldNeverReachHere(string.Format("Class not supported: {0}", base.GetType().FullName));
				return -1;
			}
		}

		private static IPoint CreatePointFromInternalCoord(Coordinate coord, IGeometry exemplar)
		{
			exemplar.PrecisionModel.MakePrecise(coord);
			return exemplar.Factory.CreatePoint(coord);
		}

		public virtual bool IsRectangle
		{
			get
			{
				return false;
			}
		}

		protected static double[] CreateArray(int size, double value)
		{
			double[] array = new double[size];
			for (int i = 0; i < size; i++)
			{
				array[i] = value;
			}
			return array;
		}

		protected static double[] CreateArray(ICoordinateSequence sequence, Ordinate ordinate)
		{
			double[] array = new double[sequence.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = sequence.GetOrdinate(i, ordinate);
			}
			return array;
		}

		private static readonly Type[] _sortedClasses = new Type[]
		{
			typeof(Point),
			typeof(MultiPoint),
			typeof(LineString),
			typeof(LinearRing),
			typeof(MultiLineString),
			typeof(Polygon),
			typeof(MultiPolygon),
			typeof(GeometryCollection)
		};

		private IGeometryFactory _factory;

		private object _userData;

		private Envelope _envelope;

		private int _srid;

		private Dimension _dimension;

		public static readonly IGeometryFactory DefaultFactory = GeometryFactory.Default;

		private class GeometryChangedFilter : IGeometryComponentFilter
		{
			public void Filter(IGeometry geom)
			{
				geom.GeometryChangedAction();
			}
		}
	}
}
